<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WhisperLiveKit 实时语音转写系统</title>
    <!-- 使用CDN的Socket.IO库 -->
    <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
    <!-- 备用CDN -->
    <script>
        // 检查Socket.IO是否加载成功
        if (typeof io === 'undefined') {
            console.log('主CDN加载失败，尝试备用CDN...');
            var script = document.createElement('script');
            script.src = 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js';
            script.onload = function() {
                console.log('备用CDN加载成功');
            };
            script.onerror = function() {
                console.log('备用CDN也失败，尝试本地版本...');
                var localScript = document.createElement('script');
                localScript.src = '/static/socket.io.js';
                localScript.onload = function() {
                    console.log('本地Socket.IO库加载成功');
                };
                localScript.onerror = function() {
                    console.error('所有Socket.IO库都无法加载');
                    alert('无法加载Socket.IO库，请检查网络连接或联系管理员');
                };
                document.head.appendChild(localScript);
            };
            document.head.appendChild(script);
        }
    </script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            color: #333;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        .header {
            text-align: center;
            margin-bottom: 30px;
            color: white;
        }
        
        .header h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
        
        .header p {
            font-size: 1.2rem;
            opacity: 0.9;
        }
        
        .main-content {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
            margin-bottom: 30px;
        }
        
        .card {
            background: white;
            border-radius: 15px;
            padding: 25px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            transition: transform 0.3s ease;
        }
        
        .card:hover {
            transform: translateY(-5px);
        }
        
        .card h2 {
            color: #667eea;
            margin-bottom: 20px;
            font-size: 1.5rem;
        }
        
        .controls {
            display: flex;
            flex-direction: column;
            gap: 15px;
        }
        
        .btn {
            padding: 12px 24px;
            border: none;
            border-radius: 8px;
            font-size: 1rem;
            cursor: pointer;
            transition: all 0.3s ease;
            font-weight: 600;
        }
        
        .btn-primary {
            background: #667eea;
            color: white;
        }
        
        .btn-primary:hover {
            background: #5a6fd8;
            transform: translateY(-2px);
        }
        
        .btn-danger {
            background: #e74c3c;
            color: white;
        }
        
        .btn-danger:hover {
            background: #c0392b;
            transform: translateY(-2px);
        }
        
        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
        }
        
        .status {
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
            font-weight: 600;
        }
        
        .status.connected {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        
        .status.disconnected {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        
        .status.transcribing {
            background: #d1ecf1;
            color: #0c5460;
            border: 1px solid #bee5eb;
        }
        
        .transcription-area {
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            padding: 20px;
            min-height: 300px;
            max-height: 500px;
            overflow-y: auto;
            font-family: 'Courier New', monospace;
            line-height: 1.6;
        }
        
        .transcription-line {
            margin-bottom: 10px;
            padding: 8px;
            border-radius: 5px;
            background: white;
            border-left: 4px solid #667eea;
        }
        
        .speaker {
            font-weight: bold;
            color: #667eea;
        }
        
        .timestamp {
            color: #6c757d;
            font-size: 0.9rem;
        }
        
        .features {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 20px;
            margin-top: 30px;
        }
        
        .feature-item {
            background: rgba(255,255,255,0.1);
            color: white;
            padding: 20px;
            border-radius: 10px;
            text-align: center;
            backdrop-filter: blur(10px);
        }
        
        .feature-item h3 {
            margin-bottom: 10px;
            font-size: 1.2rem;
        }
        
        .system-info {
            background: rgba(255,255,255,0.1);
            color: white;
            padding: 20px;
            border-radius: 10px;
            margin-top: 20px;
            backdrop-filter: blur(10px);
        }
        
        .system-info h3 {
            margin-bottom: 15px;
            text-align: center;
        }
        
        .info-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
        }
        
        .info-item {
            text-align: center;
            padding: 10px;
            background: rgba(255,255,255,0.1);
            border-radius: 8px;
        }
        
        .info-label {
            font-size: 0.9rem;
            opacity: 0.8;
            margin-bottom: 5px;
        }
        
        .info-value {
            font-size: 1.1rem;
            font-weight: bold;
        }
        
        @media (max-width: 768px) {
            .main-content {
                grid-template-columns: 1fr;
            }
            
            .header h1 {
                font-size: 2rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🎤 WhisperLiveKit</h1>
            <p>实时语音转写 + 说话人分离系统 (Flask 版本)</p>
        </div>
        
        <div class="main-content">
            <div class="card">
                <h2>🎯 控制面板</h2>
                <div id="status" class="status disconnected">未连接</div>
                <div class="controls">
                    <button id="connectBtn" class="btn btn-primary">连接服务器</button>
                    <button id="startBtn" class="btn btn-primary" disabled>开始转写</button>
                    <button id="stopBtn" class="btn btn-danger" disabled>停止转写</button>
                    <button id="forceTranscribeBtn" class="btn btn-primary" disabled>立即转写</button>
                    <button id="clearBtn" class="btn btn-primary">清空记录</button>
                    <button id="refreshBtn" class="btn btn-primary">刷新状态</button>
                </div>
            </div>
            
            <div class="card">
                <h2>📝 实时转写</h2>
                <div id="transcriptionArea" class="transcription-area">
                    <div style="text-align: center; color: #6c757d; margin-top: 100px;">
                        点击"开始转写"开始语音识别...
                    </div>
                </div>
            </div>
        </div>
        
        <div class="system-info">
            <h3>📊 系统状态</h3>
            <div class="info-grid">
                <div class="info-item">
                    <div class="info-label">Whisper 状态</div>
                    <div id="whisperStatus" class="info-value">未知</div>
                </div>
                <div class="info-item">
                    <div class="info-label">连接状态</div>
                    <div id="connectionStatus" class="info-value">未连接</div>
                </div>
                <div class="info-item">
                    <div class="info-label">转写状态</div>
                    <div id="transcriptionStatus" class="info-value">未开始</div>
                </div>
                <div class="info-item">
                    <div class="info-label">转写数量</div>
                    <div id="transcriptionCount" class="info-value">0</div>
                </div>
            </div>
        </div>
        
        <div class="features">
            <div class="feature-item">
                <h3>🚀 实时性能</h3>
                <p>基于 SimulStreaming 技术的超低延迟</p>
            </div>
            <div class="feature-item">
                <h3>👥 说话人分离</h3>
                <p>智能识别不同说话人</p>
            </div>
            <div class="feature-item">
                <h3>🔒 本地部署</h3>
                <p>完全本地化，保护隐私</p>
            </div>
            <div class="feature-item">
                <h3>📊 高准确率</h3>
                <p>WhisperStreaming 确保质量</p>
            </div>
        </div>
    </div>

    <script>
        class WhisperLiveKitClient {
            constructor() {
                this.socket = null;
                this.isConnected = false;
                this.isTranscribing = false;
                this.transcriptionCount = 0;
                
                this.initializeElements();
                this.bindEvents();
                this.updateSystemInfo();
            }
            
            initializeElements() {
                this.connectBtn = document.getElementById('connectBtn');
                this.startBtn = document.getElementById('startBtn');
                this.stopBtn = document.getElementById('stopBtn');
                this.forceTranscribeBtn = document.getElementById('forceTranscribeBtn');
                this.clearBtn = document.getElementById('clearBtn');
                this.refreshBtn = document.getElementById('refreshBtn');
                this.statusDiv = document.getElementById('status');
                this.transcriptionArea = document.getElementById('transcriptionArea');
                
                // 系统状态元素
                this.whisperStatus = document.getElementById('whisperStatus');
                this.connectionStatus = document.getElementById('connectionStatus');
                this.transcriptionStatus = document.getElementById('transcriptionStatus');
                this.transcriptionCountDiv = document.getElementById('transcriptionCount');
            }
            
            bindEvents() {
                this.connectBtn.addEventListener('click', () => this.connect());
                this.startBtn.addEventListener('click', () => this.startTranscription());
                this.stopBtn.addEventListener('click', () => this.stopTranscription());
                this.forceTranscribeBtn.addEventListener('click', () => this.forceTranscribe());
                this.clearBtn.addEventListener('click', () => this.clearTranscriptions());
                this.refreshBtn.addEventListener('click', () => this.refreshStatus());
            }
            
            connect() {
                try {
                    // 检查Socket.IO是否可用
                    if (typeof io === 'undefined') {
                        throw new Error('Socket.IO库未加载，请检查网络连接');
                    }
                    
                    // 连接到 SocketIO 服务器
                    this.socket = io();
                    
                    this.socket.on('connect', () => {
                        this.isConnected = true;
                        this.updateStatus('connected', '已连接');
                        this.connectBtn.disabled = true;
                        this.startBtn.disabled = false;
                        this.updateSystemInfo();
                        console.log('SocketIO 连接已建立');
                    });
                    
                    this.socket.on('disconnect', () => {
                        this.isConnected = false;
                        this.isTranscribing = false;
                        this.updateStatus('disconnected', '连接已断开');
                        this.connectBtn.disabled = false;
                        this.startBtn.disabled = true;
                        this.stopBtn.disabled = true;
                        this.updateSystemInfo();
                        console.log('SocketIO 连接已断开');
                    });
                    
                    this.socket.on('connection_established', (data) => {
                        console.log('连接确认:', data);
                        this.updateWhisperStatus(data.whisper_status);
                    });
                    
                    this.socket.on('transcription_started', (data) => {
                        this.isTranscribing = true;
                        this.updateStatus('transcribing', '转写中...');
                        this.startBtn.disabled = true;
                        this.stopBtn.disabled = false;
                        this.forceTranscribeBtn.disabled = false;
                        this.updateSystemInfo();
                        console.log('转写已开始');
                    });
                    
                    this.socket.on('transcription_stopped', (data) => {
                        this.isTranscribing = false;
                        this.updateStatus('connected', '已连接');
                        this.startBtn.disabled = false;
                        this.stopBtn.disabled = true;
                        this.forceTranscribeBtn.disabled = true;
                        this.updateSystemInfo();
                        console.log('转写已停止');
                    });
                    
                    this.socket.on('transcription_result', (data) => {
                        this.addTranscription(data);
                        this.transcriptionCount++;
                        this.updateSystemInfo();
                    });
                    
                    this.socket.on('transcription_triggered', (data) => {
                        console.log('手动转写触发成功:', data);
                    });
                    
                    this.socket.on('transcriptions_cleared', (data) => {
                        this.transcriptionCount = 0;
                        this.updateSystemInfo();
                        console.log('转写记录已清空');
                    });
                    
                    this.socket.on('session_info', (data) => {
                        console.log('会话信息:', data);
                        this.updateTranscriptionStatus(data.status);
                        this.transcriptionCount = data.transcription_count;
                        this.updateSystemInfo();
                    });
                    
                    this.socket.on('error', (data) => {
                        console.error('服务器错误:', data);
                        alert(`服务器错误: ${data.message}`);
                    });
                    
                } catch (error) {
                    console.error('连接失败:', error);
                    this.updateStatus('disconnected', '连接失败');
                }
            }
            
            startTranscription() {
                if (this.socket && this.isConnected) {
                    this.socket.emit('start_transcription', {
                        timestamp: new Date().toISOString()
                    });
                }
            }
            
            stopTranscription() {
                if (this.socket && this.isConnected) {
                    this.socket.emit('stop_transcription', {
                        timestamp: new Date().toISOString()
                    });
                }
            }
            
            forceTranscribe() {
                if (this.socket && this.isConnected && this.isTranscribing) {
                    this.socket.emit('force_transcribe', {
                        timestamp: new Date().toISOString()
                    });
                }
            }
            
            clearTranscriptions() {
                if (this.socket && this.isConnected) {
                    this.socket.emit('clear_transcriptions', {});
                }
                this.transcriptionArea.innerHTML = '<div style="text-align: center; color: #6c757d; margin-top: 100px;">点击"开始转写"开始语音识别...</div>';
            }
            
            refreshStatus() {
                if (this.socket && this.isConnected) {
                    this.socket.emit('get_session_info', {});
                }
            }
            
            addTranscription(data) {
                const line = document.createElement('div');
                line.className = 'transcription-line';
                
                const speaker = document.createElement('span');
                speaker.className = 'speaker';
                speaker.textContent = data.speaker + ': ';
                
                const text = document.createElement('span');
                text.textContent = data.text;
                
                const timestamp = document.createElement('div');
                timestamp.className = 'timestamp';
                timestamp.textContent = new Date(data.timestamp).toLocaleTimeString();
                
                line.appendChild(speaker);
                line.appendChild(text);
                line.appendChild(timestamp);
                
                this.transcriptionArea.appendChild(line);
                this.transcriptionArea.scrollTop = this.transcriptionArea.scrollHeight;
            }
            
            updateStatus(type, message) {
                this.statusDiv.className = `status ${type}`;
                this.statusDiv.textContent = message;
            }
            
            updateWhisperStatus(status) {
                this.whisperStatus.textContent = status ? '已初始化' : '未初始化';
                this.whisperStatus.style.color = status ? '#28a745' : '#dc3545';
            }
            
            updateConnectionStatus() {
                this.connectionStatus.textContent = this.isConnected ? '已连接' : '未连接';
                this.connectionStatus.style.color = this.isConnected ? '#28a745' : '#dc3545';
            }
            
            updateTranscriptionStatus(status) {
                let statusText = '未开始';
                let statusColor = '#6c757d';
                
                switch (status) {
                    case 'transcribing':
                        statusText = '转写中';
                        statusColor = '#17a2b8';
                        break;
                    case 'stopped':
                        statusText = '已停止';
                        statusColor = '#dc3545';
                        break;
                    case 'no_session':
                        statusText = '无会话';
                        statusColor = '#6c757d';
                        break;
                }
                
                this.transcriptionStatus.textContent = statusText;
                this.transcriptionStatus.style.color = statusColor;
            }
            
            updateSystemInfo() {
                this.updateConnectionStatus();
                this.updateTranscriptionStatus(this.isTranscribing ? 'transcribing' : 'no_session');
                this.transcriptionCountDiv.textContent = this.transcriptionCount;
            }
        }
        
        // 初始化客户端
        document.addEventListener('DOMContentLoaded', () => {
            new WhisperLiveKitClient();
        });
    </script>
</body>
</html>
